package com.klikmee.sms.core.base;

import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Logger;

import com.klikmee.sms.core.base.communicator.Communicator;
import com.klikmee.sms.core.base.communicator.DeliveryReport;
import com.klikmee.sms.core.base.communicator.SendSmsIntercepter;
import com.klikmee.sms.core.base.creators.MessageCreator;
import com.klikmee.sms.core.base.dao.BulkMessageDAO;
import com.klikmee.sms.core.base.dao.IncomingMessageFacadeLocal;
import com.klikmee.sms.core.base.dao.OutgoingMessageFacadeLocal;
import com.klikmee.sms.core.base.dao.ServiceDAO;
import com.klikmee.sms.core.base.dao.ServiceFacadeLocal;
import com.klikmee.sms.core.base.exceptions.CommunicatorNotResolvedException;
import com.klikmee.sms.core.base.exceptions.DeliveryHandlerNotResolved;
import com.klikmee.sms.core.base.exceptions.DeliveryReportNotResolvedException;
import com.klikmee.sms.core.base.handlers.ExceptionHandler;
import com.klikmee.sms.core.base.postman.InvalidServiceUrlException;
import com.klikmee.sms.core.base.postman.ServiceMessageDeliverer;
import com.klikmee.sms.core.base.resolvers.IncomingMessage2ServiceResolver;
import com.klikmee.sms.core.base.util.Constants;
import com.klikmee.sms.core.base.util.States;
import com.klikmee.sms.core.commons.events.AppEvent;
import com.klikmee.sms.core.commons.events.EventDispatcher;
import com.klikmee.sms.core.model.BulkMessage;
import com.klikmee.sms.core.model.IncomingMessage;
import com.klikmee.sms.core.model.OutgoingMessage;
import com.klikmee.sms.core.model.Provider;
import com.klikmee.sms.core.model.Service;
import com.klikmee.sms.core.model.ServiceChargingCategory;

public class Core {
	/**
	 * Instance of log
	 */
	private Logger logger = Logger.getLogger(Core.class);
	/**
	 * Dao instances
	 */
	private OutgoingMessageFacadeLocal outgoingMessageDAO = null;
	private IncomingMessageFacadeLocal incomingMessageDAO = null;
	private ServiceDAO serviceDAO = null;
	private BulkMessageDAO bulkMessageDAO = null;

	/**
	 * Message creator used to create charged and bulk messages
	 */
	private MessageCreator messageCreator = null;
	/**
	 * Component responsible for delivering messages, status resports and
	 * subscription events to service instances
	 */
	private ServiceMessageDeliverer messageDeliverer = null;

	/**
	 * Cached list off all services for better performance
	 */
	private List<Service> serviceDefinitions = null;

	/**
	 * Service instance used in situations when customer needs to be notified
	 * free of charge
	 */
	private Service freeOfChargeService = null;
	private String notEngoughCreditMessageText = null;
	private String serviceNotAvailableMessageText = null;

	/**
	 * Service instance used in situations when customer sends unrecognized
	 * request
	 */
	private Service badRequestService = null;
	private String badSintaxMessageText = null;

	/**
	 * Ids used to load badRequest and freeOfCharge services
	 */
	private Long badRequestServiceId = null;
	private Long freeOfChargeServiceId = null;

	/**
	 * Queues for buffering incoming, outgoing, bulk and delivery report
	 * messages.
	 */
	private BlockingQueue<IncomingMessage> incomingMessageQueue = new LinkedBlockingQueue<IncomingMessage>();
	private BlockingQueue<OutgoingMessage> outgoingMessageQueue = new LinkedBlockingQueue<OutgoingMessage>();
	private BlockingQueue<DeliveryReport> statusReportQueue = new LinkedBlockingQueue<DeliveryReport>();
	private BlockingQueue<BulkMessage> bulkMessageQueue = new LinkedBlockingQueue<BulkMessage>();

	/**
	 * Cache instances
	 */
	
	private Map<String, OutgoingMessage> outgoingMessageCache;
	
	
	/**
	 * Cache drivers
	 */
	
	private Map<String, CoreCacheDriver<DeliveryReport, OutgoingMessage>> outgoingMessageDriverMap;
	
	/**
	 * Worker threads for queue processing.
	 */
	private Thread incommingMessageQueueProcessingThread = null;
	private Thread outgoingMessageQueueProcessingThread = null;
	private Thread statusReportQueueProcessingThread = null;
	private Thread bulkMessageQueueProcessingThread = null;

	/**
	 * Exception handler used to handler internal, service and other errors
	 */
	private ExceptionHandler exceptionHandler = null;
	/**
	 * Map of communicators configured for this instance of core context. Key is
	 * the name of the communicator identical to the value of
	 * {@link Provider#getName()} of the provider the communicator is
	 * implemented for. The value is and instance of communicator for that
	 * provider. Example: for MTS communicator value is MTS for VIP is VIP and
	 * so on The names of each implemented communicator can be found in
	 * {@link Constants} class.
	 * 
	 */
	private Map<String, Communicator> communicatorMap = null;

	/**
	 * Map of incoming message resolvers used to resolve incoming message to
	 * service definition
	 */
	private Map<String, IncomingMessage2ServiceResolver> incomingMessageResolverMap = null;

	/**
	 * Flag indicating that is time to stop queue processing.
	 */
	private boolean stopQueueProcessing = false;
	private EventDispatcher eventDispatcher;

	/**
	 * Default constructor for core context
	 */
	public Core() {
	}

	/**
	 * Init method for initializing core resources
	 */
	public void init() {
		// cache all service definitions
		loadServicesDefinitions();
		// start all worker threads
		this.startQueueProcessingThreads();
		logger.info(MessageFormat.format("Mapped {0} communicators instances", communicatorMap.size()));
		logger.info(MessageFormat.format("Mapped {0} incoming message resolvers", incomingMessageResolverMap.size()));

		logger.info("Core initialized. ");
	}

	/**
	 * Enqueues incoming message for processing.
	 * <p>
	 * Usually called from {@link Communicator} implementation
	 * </p>
	 * 
	 * @param incomingMessage
	 */
	public void enqueueIncomingMessage(IncomingMessage incomingMessage) {
		try {
			incomingMessageQueue.put(incomingMessage);
			logger.debug(MessageFormat.format("Enqueued new incomming message from [{0}], current queue size [{1}]", incomingMessage.getFromNumber(),
					incomingMessageQueue.size()));
		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue incoming message. ", e);
		}
		catch (Exception e) {
			exceptionHandler.handle(e);
		}
	}

	/**
	 * Enqueues new message to the outgoing queue.
	 * 
	 * @param outgoingMessage
	 *            a message to be sent
	 */
	public void enqueueOutgoingMessage(OutgoingMessage outgoingMessage) {
		try {
			outgoingMessageQueue.put(outgoingMessage);
			logger.info(MessageFormat.format("Enqueued outgoing message with id = [{0}]", outgoingMessage.getId()));
		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue outgoing message. ", e);
		}
	}

	/**
	 * Adds new bulk message to the tail of the outgoing bulk queue
	 * 
	 * @param bulkMessage
	 */
	public void enqueueBulkMessage(BulkMessage bulkMessage) {
		try {
			this.bulkMessageQueue.put(bulkMessage);
			logger.info(MessageFormat.format("Enqueued bulk message with id = [{0}]", bulkMessage.getId()));
		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue bulk message. ", e);
		}
	}

	/**
	 * Enqueues status report for the send message. Usually called by
	 * communicator.
	 * 
	 * @param deliveryReport
	 */
	public void enqueueStatusReport(DeliveryReport deliveryReport) {
		try {
			this.statusReportQueue.put(deliveryReport);
			logger.info(MessageFormat.format("Enqueued new status report with msgid = [{0}] and providersMsgId = [{1}]", deliveryReport.getMessageId(),
					deliveryReport.getProvidersMessageId()));
		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to enqueue status message. ", e);
		}
	}

	/**
	 * Processes an incoming message from the queue by calling
	 * {@link #onNewMessage(IncomingMessage)} method
	 */
	public void processIncomingMessageQueue() {

		try {

			IncomingMessage incomingMessage = incomingMessageQueue.take();
			logger.debug(MessageFormat.format("Took incoming message from sender [{0}] from queue", incomingMessage.getFromNumber()));

			this.onNewMessage(incomingMessage);

		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process incoming messages ", e);
		}
		catch (Exception e) {
			exceptionHandler.handle(e);
		}
	}

	/**
	 * Takes next message from the outgoing queue and calls
	 * {@link #forwardToCommunicator(OutgoingMessage)} with the message as an
	 * argument.
	 */
	public void processOutgoingMessageQueue() {

		try {
			// take message from queue and cache it
			OutgoingMessage outgoingMessage = outgoingMessageQueue.take();
			logger.debug(MessageFormat.format("Took outgoing message with id = [{0}] from queue", outgoingMessage.getId()));
			// resolve cache driver
			CoreCacheDriver<DeliveryReport, OutgoingMessage> coreCacheDriver = outgoingMessageDriverMap.get(outgoingMessage.getProvider().getName());
			coreCacheDriver.write(outgoingMessage);
			
			this.forwardToCommunicator(outgoingMessage);
		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process outgoing messages ", e);
		}
		catch (Exception e) {
			exceptionHandler.handle(e);
		}

	}

	/**
	 * Takes next bulk message from queue and forwards it to the resolved
	 * communicator
	 */
	public void processBulkMessageQueue() {

		try {

			BulkMessage bulkMessage = bulkMessageQueue.take();

			logger.debug(MessageFormat.format("Took bulk message with id = [{0}] from queue", bulkMessage.getId()));
			forwardToCommunicator(bulkMessage);
		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process bulk messages ", e);
		}
		catch (Exception e) {
			logger.fatal("Unexpected error while processing bulk queue ", e);
		}
	}

	/**
	 * <p>
	 * Takes next status report from the queue and processes it by calling
	 * {@link #onDeliveryReport(OutgoingMessage)}
	 * </p>
	 */
	public void processStatusReportQueue() {

		try {
			DeliveryReport message = this.statusReportQueue.take();
			logger.debug(MessageFormat.format("Took status report from provider [{0}] from queue", message.getProvider().getName()));
			this.onDeliveryReport(message);

		}
		catch (InterruptedException e) {
			logger.fatal("Interupted while waiting to process status report.", e);
		}
		catch (Exception e) {
			exceptionHandler.handle(e);
		}
	}

	/**
	 * Forwards bulk message to appropriate communicator
	 * 
	 * @param bulkMessage
	 */
	private void forwardToCommunicator(BulkMessage bulkMessage) {
		// resolve communicator
		Provider provider = bulkMessage.getProvider();

		if (provider != null) {
			try {
				Communicator communicator = resolveCommunicator(provider);

				communicator.sendBulkMessage(bulkMessage);

				logger.info(MessageFormat.format("Bulk message with id = [{0}] forwarded to communicator", bulkMessage.getId()));
			}
			catch (CommunicatorNotResolvedException e) {
				exceptionHandler.handle(e);
			}
			catch (Exception e) {
				exceptionHandler.handle(e);
			}
		}
		else {
			logger.warn(MessageFormat.format("Communicator for bulk message with id = [{0}] and receiver [{1}] could not be resolved", bulkMessage.getId(),
					bulkMessage.getToNumber()));
		}

	}

	/**
	 * Sends the message via appropriate communicator. Communicator is resolved
	 * from {@link #communicatorMap} and each {@link SendSmsIntercepter} for
	 * that communicator is executed. Finaly message is sent by calling
	 * {@link Communicator#sendMessage(OutgoingMessage)} on the resolved
	 * communicator.
	 * 
	 * @param outgoingMessage
	 */
	private void forwardToCommunicator(OutgoingMessage outgoingMessage) {
		// resolve communicator used to send this message
		Communicator communicator = null;
		try {
			communicator = resolveCommunicator(outgoingMessage.getProvider());

			// execute intercepters assigned to this communicator, if any
			List<SendSmsIntercepter> smsIntercepters = communicator.getSendSmsIntercepters();

			for (SendSmsIntercepter intercepter : smsIntercepters) {
				intercepter.processMessage(outgoingMessage);
			}

			// forward message to communicator
			communicator.sendMessage(outgoingMessage);
			logger.info(MessageFormat.format("Message with id = [{0}] sent to {1} communicator", outgoingMessage.getId(), communicator));
		}
		catch (CommunicatorNotResolvedException e) {
			exceptionHandler.handle(e);
		}

	}

/**
     * callback from {@link #sendMessage(com.klikmee.sms.core.entities.OutgoingMessage)
     * @param outgoingMessage - should be same object as it was in method sendMessage
     * 
     */
	public void onMessageSent(OutgoingMessage outgoingMessage) {

		if (outgoingMessage == null) {
			logger.fatal("Could not process null core.OnMessageSent(), returning");
			return;
		}

		// set outgoing message state
		outgoingMessage.setState(States.OutgoingMessage.SENT);
		outgoingMessage.setOurOutGoingTime(new Date());
		outgoingMessageDAO.edit(outgoingMessage);

		// remove message from cache
		CoreCacheDriver<DeliveryReport, OutgoingMessage> coreCacheDriver = outgoingMessageDriverMap.get(outgoingMessage.getProvider().getName());
		coreCacheDriver.remove(outgoingMessage);
		
		logger.info(String.format("Message successfully sent id = [%s]", outgoingMessage.getId()));
	}

	/**
	 * Invoked by some of the communicators after successfully sending message.
	 * 
	 * @param bulkMessage
	 *            bulk message sent by communicator.
	 */

	public void onBulkMessageSent(BulkMessage bulkMessage) {
		// verify message
		if (bulkMessage == null) {
			return;
		}

		// just update state
		bulkMessage.setState(States.BulkMessage.SENT);
		bulkMessage.setSendingTime(new Date());

		bulkMessageDAO.edit(bulkMessage);

	}

	/**
	 * Callback from communicator when it receives new message. This method
	 * links the message to the service based on it's syntax. If massage has a
	 * wrong syntax it is linked to bad request service.
	 * 
	 * @param sms
	 * 
	 */
	public void onNewMessage(IncomingMessage incomingMessage) {
		logger.info(MessageFormat.format("Received new message request from [{0}], to [{1}], message [{2}]", incomingMessage.getFromNumber(),
				incomingMessage.getToNumber(), incomingMessage.getMessage()));

		// resolve service for this message
		Provider provider = incomingMessage.getProvider();
		IncomingMessage2ServiceResolver incomingMessage2ServiceResolver = incomingMessageResolverMap.get(provider.getName());

		Service resolvedService = incomingMessage2ServiceResolver.resolve(incomingMessage);

		// if message could not be resolve to any service link message to the
		// bad request service and send info message
		// back to the user
		if (resolvedService == null) {
			incomingMessage.setService(badRequestService);
			incomingMessage.setState(States.IncommingMessage.BAD_SYNTAX_NOTIFICATION_SENT);
			incomingMessageDAO.create(incomingMessage);

			ServiceChargingCategory serviceChargingCategory = getBadRequestService().getServiceChargingCategoryList().iterator().next();
			messageCreator.createResponse(incomingMessage, serviceChargingCategory, badSintaxMessageText);

			logger.info(MessageFormat.format("Could not resolve message to the service, user notification sent, message id = [{0}]", incomingMessage.getId()));
		}

		// if service for this message is resolved,
		// forward the message to the concrete service instance
		else {
			incomingMessage.setService(resolvedService);
			incomingMessage.setState(States.IncommingMessage.SYNTACTICALLY_OK);
			incomingMessageDAO.create(incomingMessage);

			// try to deliver message to service
			boolean messageDelivered = false;
			try {
				messageDelivered = messageDeliverer.deliverMessage(incomingMessage);
			}
			catch (InvalidServiceUrlException e) {

				exceptionHandler.handle(e);
			}

			if (!messageDelivered) {
				// if message is not delivered to service, send info message to
				// user

				messageCreator.createResponse(incomingMessage, this.getFreeOfChargeService().getServiceChargingCategoryList().iterator().next(),
						this.serviceNotAvailableMessageText);
				// and set im state
				incomingMessage.setState(States.IncommingMessage.SERVICE_NOT_AVAILABLE_NOTIFICATION_SENT);
				incomingMessageDAO.edit(incomingMessage);

				logger.info(MessageFormat.format("Could not deliver message to service named [{0}], message id = [{1}], user notification sent.",
						incomingMessage.getService().getName(), incomingMessage.getId()));
			}

		}

	}

	/**
	 * Announces that new delivery report is available by using
	 * {@link EventDispatcher} instance to dispatch it to registered listeners.
	 * 
	 * @param deliveryReport
	 * @throws DeliveryReportNotResolvedException
	 * @throws DeliveryHandlerNotResolved
	 */
	public void onDeliveryReport(DeliveryReport deliveryReport) {

		// create event instance and dispatch it
		AppEvent event = new AppEvent(EventManager.DELIVERY_REPORT_ANNOUNCED, deliveryReport);
		this.eventDispatcher.dispatch(event, false);
	}

	/**
	 * Resolves an instance of concrete communicator from
	 * {@link #communicatorMap} to be used to send outgoing message. Matching is
	 * done using {@link Provider#getName()} attached to the outgoing message
	 * 
	 * @param provider
	 *            the actual message to be sent
	 * @return an instance of {@link Communicator} used to send the message, or
	 *         null if no communicator could be resolved
	 * @throws CommunicatorNotResolvedException
	 */
	private Communicator resolveCommunicator(Provider provider) throws CommunicatorNotResolvedException {

		// get the name of provider associated with this message
		String providersName = provider.getName();

		// use this value to get instance of communicator from the map
		Communicator communicator = this.communicatorMap.get(providersName);

		if (communicator == null) {
			throw new CommunicatorNotResolvedException(MessageFormat.format("Failed to resolve communicator for message id = [{0}] by providers name = {1}",
					provider.getId(), providersName));
		}

		logger.info(MessageFormat.format("Resolved communicator instance for provider [{0}]", provider.getName()));
		return communicator;
	}

	/**
	 * Starts threads for queue processing
	 */
	private void startQueueProcessingThreads() {
		// incoming message queue

		this.incommingMessageQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processIncomingMessageQueue();
				}

			}
		});

		this.incommingMessageQueueProcessingThread.start();
		logger.info("Started processing incoming message queue...");

		// outgoing message queue
		this.outgoingMessageQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processOutgoingMessageQueue();
				}

			}
		});

		this.outgoingMessageQueueProcessingThread.start();
		logger.info("Started processing outgoing message queue...");

		// status report queue
		this.statusReportQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processStatusReportQueue();
				}

			}
		});
		this.statusReportQueueProcessingThread.start();
		logger.info("Started processing status report queue...");
		// status report queue

		this.bulkMessageQueueProcessingThread = new Thread(new Runnable() {

			public void run() {
				while (!stopQueueProcessing) {
					Core.this.processBulkMessageQueue();
				}

			}
		});

		this.bulkMessageQueueProcessingThread.start();
		logger.info("Started processing bulk message queue...");

	}

	/*
	 * utility method for loading service definitions from db. Loads all
	 * services plus bad request and free of charge service.
	 */
	public void loadServicesDefinitions() {

		badRequestService = serviceDAO.find(this.badRequestServiceId);
		logger.debug( MessageFormat.format( "Loaded [{0}] service from database", badRequestService != null ? badRequestService.getName() : "Failed" ) );
		freeOfChargeService = serviceDAO.find(this.freeOfChargeServiceId);
		logger.debug( MessageFormat.format( "Loaded [{0}] service from database", freeOfChargeService != null ? freeOfChargeService.getName() : "Failed") );
		
		// load all service definitions
		this.serviceDefinitions = serviceDAO.findAllPrioritized();
		logger.info(MessageFormat.format("Loaded {0} service definitions", serviceDefinitions.size()));
	}

	public void destroy() {
		stopQueueProcessing = true;
		// interupt the threads
		this.incommingMessageQueueProcessingThread.interrupt();
		this.outgoingMessageQueueProcessingThread.interrupt();
		this.bulkMessageQueueProcessingThread.interrupt();
		this.statusReportQueueProcessingThread.interrupt();

		logger.info("Shuting down core completed.");
	}

	public List<Service> getServiceDefinitions() {
		return serviceDefinitions;
	}

	public Service getFreeOfChargeService() {
		return freeOfChargeService;
	}

	public Service getBadRequestService() {
		return badRequestService;
	}

	public void setOutgoingMessageDAO(OutgoingMessageFacadeLocal outgoingMessageDAO) {
		this.outgoingMessageDAO = outgoingMessageDAO;
	}

	public String getNotEngoughCreditMessageText() {
		return notEngoughCreditMessageText;
	}

	public void setServiceDAO(ServiceDAO serviceDAO) {
		this.serviceDAO = serviceDAO;
	}

	public void setMessageCreator(MessageCreator billing) {
		this.messageCreator = billing;
	}

	public void setIncomintMessageDAO(IncomingMessageFacadeLocal incomintMessageDAO) {
		this.incomingMessageDAO = incomintMessageDAO;
	}

	public void setMessageDeliverer(ServiceMessageDeliverer messageDeliverer) {
		this.messageDeliverer = messageDeliverer;
	}

	public void setBadSintaxMessageText(String badSintaxMessageText) {
		this.badSintaxMessageText = badSintaxMessageText;
	}

	public void setServiceNotAvailableMessageText(String serviceNotAvailableMessageText) {
		this.serviceNotAvailableMessageText = serviceNotAvailableMessageText;
	}

	public void setBulkMessageDAO(BulkMessageDAO bulkMessageDAO) {
		this.bulkMessageDAO = bulkMessageDAO;
	}

	public void setNotEngoughCreditMessageText(String notEngoughCreditMessageText) {
		this.notEngoughCreditMessageText = notEngoughCreditMessageText;
	}

	public String getBadSintaxMessageText() {
		return badSintaxMessageText;
	}

	public OutgoingMessageFacadeLocal getOutgoingMessageDAO() {
		return outgoingMessageDAO;
	}

	public IncomingMessageFacadeLocal getIncomingMessageDAO() {
		return incomingMessageDAO;
	}

	public BulkMessageDAO getBulkMessageDAO() {
		return bulkMessageDAO;
	}

	public MessageCreator getMessageCreator() {
		return messageCreator;
	}

	public ServiceMessageDeliverer getMessageDeliverer() {
		return messageDeliverer;
	}

	public String getServiceNotAvailableMessageText() {
		return serviceNotAvailableMessageText;
	}

	public Logger getLogger() {
		return logger;
	}

	public BlockingQueue<IncomingMessage> getIncomingMessageQueue() {
		return incomingMessageQueue;
	}

	public BlockingQueue<OutgoingMessage> getOutgoingMessageQueue() {
		return outgoingMessageQueue;
	}

	public BlockingQueue<DeliveryReport> getStatusReportQueue() {
		return statusReportQueue;
	}

	public BlockingQueue<BulkMessage> getBulkMessageQueue() {
		return bulkMessageQueue;
	}

	public Thread getIncommingMessageQueueProcessingThread() {
		return incommingMessageQueueProcessingThread;
	}

	public Thread getOutgoingMessageQueueProcessingThread() {
		return outgoingMessageQueueProcessingThread;
	}

	public Thread getStatusReportQueueProcessingThread() {
		return statusReportQueueProcessingThread;
	}

	public Thread getBulkMessageQueueProcessingThread() {
		return bulkMessageQueueProcessingThread;
	}

	public boolean isStopQueueProcessing() {
		return stopQueueProcessing;
	}

	public Long getBadRequestServiceId() {
		return badRequestServiceId;
	}

	public void setBadRequestServiceId(Long badRequestServiceId) {
		this.badRequestServiceId = badRequestServiceId;
	}

	public Long getFreeOfChargeServiceId() {
		return freeOfChargeServiceId;
	}

	public void setFreeOfChargeServiceId(Long freeOfChargeServiceId) {
		this.freeOfChargeServiceId = freeOfChargeServiceId;
	}

	public void setExceptionHandler(ExceptionHandler exceptionHandler) {
		this.exceptionHandler = exceptionHandler;
	}

	public void setCommunicatorMap(Map<String, Communicator> communicatorMap) {
		this.communicatorMap = communicatorMap;
	}

	public void setIncomingMessageResolverMap(Map<String, IncomingMessage2ServiceResolver> incomingMessageResolverMap) {
		this.incomingMessageResolverMap = incomingMessageResolverMap;
	}

	public Map<String, Communicator> getCommunicatorMap() {
		return communicatorMap;
	}
	
	public void setEventDispatcher(EventDispatcher eventDispatcher) {
		this.eventDispatcher = eventDispatcher;
	}
	
	public EventDispatcher getEventDispatcher() {
		return eventDispatcher;
	}

	public void setOutgoingMessageCache(Map<String, OutgoingMessage> outgoingMessageCache) {
		this.outgoingMessageCache = outgoingMessageCache;
	}
	
	public Map<String, OutgoingMessage> getOutgoingMessageCache() {
		return outgoingMessageCache;
	}
	
	public void setOutgoingMessageDriverMap(Map<String, CoreCacheDriver<DeliveryReport, OutgoingMessage>> outgoingMessageDriverMap) {
		this.outgoingMessageDriverMap = outgoingMessageDriverMap;
	}
}
